package controllers.chat;

import models.ApplicationUser;
import models.chat.Channel;
import models.chat.Status;
import models.chat.engines.MessageEngine;
import models.chat.engines.QueueMessage;
import models.chat.messages.ChannelMessage;
import models.chat.messages.system.MemberChangedStatusMessage;
import models.chat.messages.system.MemberJoinedChannelMessage;
import models.chat.messages.system.MemberLeftChannelMessage;
import play.Logger;
import play.libs.F;
import play.mvc.Http;
import play.mvc.WebSocketController;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static play.libs.F.EventStream;
import static play.mvc.Http.WebSocketEvent.SocketClosed;
import static play.mvc.Http.WebSocketEvent.TextFrame;

/**
 * @author Konovalov_Nik
 */
public class ChatWS extends WebSocketController {

    public static Map<IdPair, EventStream<QueueMessage>> toClientMessages = new HashMap<IdPair, EventStream<QueueMessage>>();
    public static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);

    public static void servToClient(long chatId) {
        long uid = Long.parseLong(session.get("uid"));
        Logger.debug("serv to client ws connected with chatId = " + chatId + " uid=" + uid);
        IdPair pair = new IdPair(uid, chatId);
        lock.writeLock().lock();
        EventStream<QueueMessage> queue;
        try {
            if (toClientMessages.containsKey(pair)) {
                toClientMessages.remove(pair);
            }
            queue = new EventStream<QueueMessage>();
            toClientMessages.put(pair, queue);
        } finally {
            lock.writeLock().unlock();
        }


        while (inbound.isOpen()) {
            QueueMessage message = await(queue.nextEvent());
            Map<String, String> toClientJson = new HashMap<String, String>();

            if (message instanceof ChannelMessage) {
                ChannelMessage channelMessage = (ChannelMessage) message;
                ApplicationUser member = ApplicationUser.findById(channelMessage.memberFrom);
                toClientJson.put("type", "ChannelMessage");
                toClientJson.put("from", member.name);
                toClientJson.put("text", channelMessage.text);
            }

            if (message instanceof MemberChangedStatusMessage) {
                MemberChangedStatusMessage changedStatusMessage = (MemberChangedStatusMessage) message;
                toClientJson.put("type", "ChangeStatusMessage");
                toClientJson.put("user", changedStatusMessage.member.name);
                toClientJson.put("status", changedStatusMessage.member.status.getPageStyle());

            }

            if (message instanceof MemberJoinedChannelMessage) {
                MemberJoinedChannelMessage joinedChannelMessage = (MemberJoinedChannelMessage) message;
                toClientJson.put("type", "ChangeStatusMessage"); // enter means not offline
                toClientJson.put("user", joinedChannelMessage.member.name);
                toClientJson.put("status", joinedChannelMessage.member.status.getPageStyle());

            }

            if (message instanceof MemberLeftChannelMessage) {
                MemberLeftChannelMessage leftChannelMessage = (MemberLeftChannelMessage) message;
                toClientJson.put("type", "ChangeStatusMessage"); // leave means become offline
                toClientJson.put("user", leftChannelMessage.member.name);
                toClientJson.put("status", Status.OFFLINE.getPageStyle());
            }
            outbound.sendJson(toClientJson);
        }
    }

    public static void clientToServ(long chatId) {
        long uid = Long.parseLong(session.get("uid"));
        Logger.debug("client to serv ws connected with chatId = " + chatId + " uid=" + uid);
        IdPair pair = new IdPair(uid, chatId);

        while (inbound.isOpen()) {
            Http.WebSocketEvent e = await(inbound.nextEvent());
            for (String msg : TextFrame.match(e)) {
                ApplicationUser sender = ApplicationUser.findById(uid);
                Channel destChannel = Channel.findById(chatId);
                ChannelMessage channelMessage = new ChannelMessage(msg, sender, destChannel);
                MessageEngine.addMessage(channelMessage);
            }
            for (Http.WebSocketClose close : SocketClosed.match(e)) {
                Logger.debug("disconnected serv to client");
                ApplicationUser current = ApplicationUser.findById(uid);
                Channel channel = Channel.findById(chatId);
                MessageEngine.addMessage(new MemberLeftChannelMessage(current, channel));
                lock.writeLock().lock();
                try {
                    toClientMessages.remove(pair);
                } finally {
                    lock.writeLock().unlock();
                }
            }
        }
    }

}
